package play.modules.googleclosure;

import java.io.File;
import java.io.FileWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import play.Logger;
import play.Play;
import play.PlayPlugin;
import play.exceptions.UnexpectedException;
import play.vfs.VirtualFile;

import com.google.template.soy.SoyFileSet;
import com.google.template.soy.SoyFileSet.Builder;
import com.google.template.soy.base.SoySyntaxException;
import com.google.template.soy.jssrc.SoyJsSrcOptions;

public class ClosurePlugin extends PlayPlugin{

	/**
	 * Map of relative VirtualFile paths to the corresponding template 
	 */
	private Map<String, ClosureTemplate> templates = new HashMap<String, ClosureTemplate>();
	
	private VirtualFile outputFolder;
	private VirtualFile soyFolder;
	private boolean preserveFolders = false;
	
	/**
	 * Scans a folder for modified soy templates recursively.
	 * 
	 * @param path The path to start scanning
	 * @param modifiedTemplates List where all found templates are added
	 */
	private void scanSoyPath(VirtualFile path, List<ClosureTemplate> modifiedTemplates) {
		if(path.isDirectory()){
			for(VirtualFile subpath: path.list()) {
				scanSoyPath(subpath, modifiedTemplates);
			}
		}
		else {
			if(path.getName().endsWith(".soy") && !path.getName().startsWith(".")){
				ClosureTemplate tpl = templates.get(path.relativePath());

				if(tpl != null){
					if(tpl.timestamp < path.lastModified()){
						modifiedTemplates.add(tpl);
					}
				}
				else {
					tpl = new ClosureTemplate(path.getName(), path);
					tpl.timestamp = path.lastModified();
					templates.put(path.relativePath(), tpl);
					modifiedTemplates.add(tpl);
				}

			}
		}
	}

	/**
	 * Compiles soy templates to javascript files
	 * 
	 * @param sourceTemplates List of templates that are compiled
	 */
	private void compileSoyTemplates(List<ClosureTemplate> sourceTemplates){
		Builder builder = new Builder();
		
		for(ClosureTemplate tpl : sourceTemplates){
			VirtualFile tplFile = tpl.templateFile;
			
			String jsFile;
			if(preserveFolders) {
				jsFile = tplFile.relativePath().substring(soyFolder.relativePath().length());
			} else {
				jsFile = File.separator + tplFile.getName();
			}
						
			VirtualFile tplJs = VirtualFile.open(
					outputFolder.getRealFile().getAbsolutePath()
					+ jsFile.substring(0, jsFile.lastIndexOf(".soy"))
					+ ".js");

			builder.add(tplFile.getRealFile());	
			tpl.jsFile = tplJs;
		}
		
		try {		
			SoyFileSet soyFileSet = builder.build();
			SoyJsSrcOptions options = new SoyJsSrcOptions();
			List<String> res = soyFileSet.compileToJsSrc(options, null);
			
			for(int i=0; i<sourceTemplates.size();i++){
				try {
					ClosureTemplate tpl = sourceTemplates.get(i);
					
					// emit bytecode to standard class layout as well
					File f = tpl.jsFile.getRealFile();
					f.getParentFile().mkdirs();
					
					Logger.debug("ClosurePlugin : Soy2JS compiled %s into %s", 
							tpl.templateFile.relativePath(), 
							tpl.jsFile.relativePath());
					
					FileWriter fw = new FileWriter(f);
					fw.write(res.get(i));
					fw.close();
					
					tpl.timestamp = tpl.jsFile.lastModified();
				} catch (Exception e) {
					e.printStackTrace();
				}				
			}
		}catch(SoySyntaxException ex){
			ex.printStackTrace();
			throw new UnexpectedException("While applying soycompiler: ", ex);
		}
	}
	
	private void scan() {
		List<ClosureTemplate> modifiedTemplates = new ArrayList<ClosureTemplate>();
		scanSoyPath(soyFolder, modifiedTemplates);
		
		if(modifiedTemplates.size() != 0){
			Logger.debug("ClosurePlugin : change detected");
			compileSoyTemplates(modifiedTemplates);
		}
	}
	
	@Override
	public void onLoad() {
		Logger.debug("ClosurePlugin : onLoad");
		
		// read the configuration
		String outputPath = Play.configuration.getProperty("googleclosure.templates.output", "public/javascripts/");
		String rootPath = Play.configuration.getProperty("googleclosure.templates.root", "app/views/");
		
		outputFolder = VirtualFile.fromRelativePath(outputPath);
		soyFolder = VirtualFile.fromRelativePath(rootPath);
		preserveFolders = "true".equals(Play.configuration.getProperty("googleclosure.templates.preservefolders", "false"));
		
		if(Play.mode != Play.Mode.PROD) scan();
	}

	@Override
	public void detectChange() {
		//Logger.debug("ClosurePlugin : detectChange");		
		if(Play.mode != Play.Mode.PROD) scan();
	}

	
}
